<?php
declare(strict_types=1);
namespace App\Common;


use Carbon\Carbon;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use function Hyperf\Config\config;
use Hyperf\Context\ApplicationContext;
use Hyperf\HttpServer\Contract\RequestInterface;
use Psr\Log\LoggerInterface;
class Common
{


    #[Inject]
    protected static LoggerInterface $logger; //存储日志



    /**
     * @param  $RedisName string redis配置的名字
     * @return \Hyperf\Redis\RedisProxy
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public static function Redis(string $RedisName = ''){
        $RedisName = $RedisName ?: 'default';
        $container = ApplicationContext::getContainer();
        return $container->get(\Hyperf\Redis\RedisFactory::class)->get($RedisName);
    }

    /**
     * 生成唯一的字符串
     * @param int $num 自定义数字
     * @return string
     */
    public static function doOrderSn(int $num):string{
        return date('YmdHis') . $num . substr(microtime(), 2, 3) . sprintf('%02d', rand(0, 99));
    }

    /**
     * @param $url //判断地址是否为绝对路径，并拼接地址
     * @return mixed|string
     */
    public static function domain_name_path(string $url){

        $preg = "/^http(s)?:\\/\\/.+/";
        if(!preg_match($preg,$url)){
            return config('host.adminDomain').$url;
        }else{
            return $url;
        }
    }


    /**
     * @return void 正则表达式
     * $str 效验的字符串
     * $type 类型 'phone' => 判断是否是电话号码 ， 'image' => 判断是否是图片
     *
     */
    public static function PregMatch(string $str,string $type){
        switch ($type){
            case 'phone':
                $status =  preg_match("/^\d{10}$/", $str);
                break;
            case 'image':
                $status = preg_match('/.*(\.png|\.jpg|\.jpeg|\.gif)$/', $str);
                break;
            case 'idcard':
                $status = preg_match('/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/i', $str);
                break;
            case 'email':
                $status = preg_match("/([a-z0-9]*[-_.]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[.][a-z]{2,3}([.][a-z]{2})?/i", $str);
                break;
            case 'ifsc' :
                $status = preg_match("/^[A-Za-z]{4}0[A-Z0-9a-z]{6}$/", $str);
                break;
            case 'cpf' :
                $status = preg_match("/^\d{11}$/", $str);
                break;
            case 'cnpj' :
                $status = preg_match("/^\d{14}$/", $str);
                break;
            case 'account' :
                $status = preg_match('/^[A-Za-z0-9]{6,25}$/', $str);
                break;
            default:
                $status = false;
        }
        return $status;
    }


    /**
     *用户token生成
     * @param  $uid int 用户UID
     * @return void
     */
    public static function setToken(int $uid)
    {
        return md5($uid.time());
    }


    /**
     * 获取用户Ip地址
     * @param $getServerParams array  这个值为$this->request->getServerParams()
     * @return string
     */
    public static function getIp(array $getServerParams = []):string{
        $request = ApplicationContext::getContainer()->get(RequestInterface::class);
        $headers = $request->getHeaders();
        if(isset($headers['x-forwarded-for'][0]) && !empty($headers['x-forwarded-for'][0])) {
            return $headers['x-forwarded-for'][0];
        } elseif (isset($headers['x-real-ip'][0]) && !empty($headers['x-real-ip'][0])) {
            return $headers['x-real-ip'][0];
        }

        $serverParams = $request->getServerParams();
        return $serverParams['remote_addr'] ?? '';

    }


    /**
     * 获取单个参数配置
     * @param $menu
     * @param int $type 1=返回数组，2=直接返回
     * @return
     */
    public static function getConfigValue($menu,int $type = 1)
    {
        $Redis = self::Redis('RedisMy6379_2');
        $value = $Redis->hGet('system_config',$menu);
        if(!$value)$value = self::getAndsetConfigValue($Redis,$menu);
        if(!$value)return '';
        return json_decode($value, true);
    }


    /**
     * 获得多个参数
     * @param $menus
     * @return array
     */
    public static function getMore($menus)
    {
        $menus = is_array($menus) ? $menus : explode(',',$menus);
        $Redis = self::Redis('RedisMy6379_2');
        $redisOldList = $Redis->hMGet('system_config',$menus);
        $redisList = self::getRedisList($redisOldList);
        $list = [];
        if(!$redisList){
            $system_config = Db::connection('readConfig')->table('system_config')->whereIn('menu_name',$menus)->pluck('value','menu_name');
            if(!$system_config)foreach ($menus as $menu) $system_config[$menu] = '';
            foreach ($system_config as $menu_name => $value){
                $Redis->hSet('system_config',$menu_name,$value);
                $list[$menu_name] = json_decode($value, true);
            }
        }else{
            foreach ($menus as  $menu)$list[$menu] = isset($redisList[$menu]) ? json_decode($redisList[$menu], true) : self::getAndsetConfigValue($Redis,$menu);
        }

        return $list;
    }

    /**
     * 整理redis的值
     * @param array $list
     * @return array
     */
    public static function getRedisList($list):array{
        $data = [];
        foreach ($list as $key => $value){
            if($value)$data[$key] = $value;
        }
        return $data;
    }


    private static function getAndsetConfigValue($Redis,$menu){
        $system_config = Db::connection('readConfig')->table('system_config')->select('value')->where('menu_name',$menu)->first();
        if(!$system_config)return '';
        $Redis->hSet('system_config',$menu,$system_config['value']);
        return $system_config['value'];
    }

    /** 第三方储存log
     * @param $ordersn 订单号
     * @param $response  第三方返回参数
     * @param $type  类型:1=充值,2=客户端提现,3=pg,4=后台提现,5=打点,6=短信,7=推广返利提现,8=回调退款失败
     * @return void
     */
    public static function log($ordersn,$response,$type){

        Db::table('log')->insert(['out_trade_no'=> $ordersn,'log' => json_encode($response,JSON_UNESCAPED_UNICODE),'type'=>$type,'createtime' => time()]);
        if($type == 2 || $type == 4 || $type == 8){
            $withdraw_log = Db::table('withdraw_log')->selectRaw('id,uid,money,withdraw_money_other')->where('ordersn',$ordersn)->first();
            Db::table('withdraw_log')->where('ordersn',$ordersn)->update(['status' => 2,'finishtime' => time()]);
            //将三方错误日志，存储到第三张表中,是查询速度快一点
            Db::table('withdraw_logcenter')->where('withdraw_id',$withdraw_log['id'])->update([
                'log_error' => json_encode($response,JSON_UNESCAPED_UNICODE),
            ]);
            //增加用户余额
            User::userEditCoin($withdraw_log['uid'],$withdraw_log['money'],5, "玩家系统处理提现失败" . $withdraw_log['uid'] . "退还提现金额" . bcdiv((string)$withdraw_log['money'],'100',2),3,1,$withdraw_log['withdraw_money_other']);

        }
    }

    /**
     * 按照比例概率发奖，概率需要为100%
     * @param array $probabilityMap 概率数组
     * @param string $probabilityField 概率字段
     * @return string
     */
    public static function proportionalProbabilityAward(array $probabilityMap, string $probabilityField = ''):string{
        // 创建一个累积概率数组
        $cumulativeProbabilities = [];
        $sum = 0;
        if($probabilityField){
            foreach ($probabilityMap as $key => $value) {
                if($value[$probabilityField] <= 0)continue; //如果概率小于等于0直接跳过
                $sum += $value[$probabilityField];
                $cumulativeProbabilities[$key] = $sum;
            }
        }else{
            foreach ($probabilityMap as $key => $value) {
                $sum += $value;
                $cumulativeProbabilities[$key] = $sum;
            }
        }


        // 生成一个介于0和1之间的随机数
        $randomNumber = mt_rand() / mt_getrandmax();

        // 遍历累积概率数组找到第一个大于或等于随机数的键
        foreach ($cumulativeProbabilities as $key => $value) {
            if ($randomNumber <= $value) {
                return (string)$key;
            }
        }

        // 如果未返回任何值，则返回最后一个键（理论上不会执行到此处）
        if(!$probabilityField){
            return (string)array_search(max($probabilityMap), $probabilityMap);
        }
        return (string)(count($probabilityMap) - 1);

    }

    /**
     * 获取UID的最后一位
     * @param $uid  用户id
     * @return void
     */
    public function getUidTail($uid){
        return substr((string)$uid, -1);
    }

    /**
     * 无限代每周数据统计
     * @param $uid  UID
     * @param $money  用户充值或者提现金额
     * @param $fee 手续费
     * @param $type 类型:1=修改充值，2=修改提现
     */
    public static function agentTeamWeeklog($uid,$money,$fee,$type = 1){
        $field = $type == 1 ? 'pay_price' : 'withdraw_price';
        //[$start,$end] = self::getWeekStartEnd(time());
        [$start,$end] = self::getStartEnd(time());

        $res = Db::table('agent_team_weeklog')
            ->where([['time' ,'>=', $start],['time','<',$end],['uid','=',$uid]])
            ->update([
                $field => Db::raw($field."+".$money),
                'fee' => Db::raw('fee + '.$fee),
                'updatetime' => time(),
            ]);
        if(!$res){
            Db::table('agent_team_weeklog')
                ->insert([
                    $field => $money,
                    'fee' => $fee,
                    'time' => $start,
                    'uid' => $uid,
                ]);
        }
    }

    /**
     * 返回指定时间戳的本周开始时间和结束时间
     * @param $time  时间戳
     * @return void
     */
    public static function getWeekStartEnd($time){
        $time = is_numeric($time) ? $time : strtotime((string)$time);
        // 获取本周的开始时间
        $startOfWeek = strtotime('Monday this week', $time);


        // 获取本周的结束时间
        $endOfWeek = strtotime('Sunday this week', $time) + 86399;

        return [$startOfWeek,$endOfWeek];
    }

    /**
     * 处理代理层级
     * @param $uid
     * @param $puid
     * @param $type
     * @param $level
     * @param $share_strlog
     * @return int
     */
    public static function setTeamLevel($uid = 0,$puid = 0,$type = 0,$level = 0,$share_strlog = [], $level_z = 1){

        $is_agent = 0;
        //第一次进来，如果有团队详细数据就直接返回。
        if(!$level){
            self::installTeamLevel($uid,$uid,0);
            //存储团队信息
            if($puid > 0){
                $nickname = !empty($share_strlog['phone']) ? self::maskString($share_strlog['phone']) : $share_strlog['nickname'];
                Db::table('user_agent_team')->insert([
                    'uid' => $uid,
                    'puid' => $puid,
                    'type' => $type,
                    'level' => $level_z,
                    'createtime' => time(),
                    'avatar' => $share_strlog['avatar'],
                    'nickname' => $nickname,
                ]);

                if ($type == 3){//设置推广员时
                    $agent_adrate = Db::table('agent_adrate')->where('type',2)->orderBy('id','desc')->first();
                    if(!$agent_adrate) return 0;
                    //添加代理
                    $res = Db::table('agent')->insert([
                        'uid' => $uid,
                        'puid' => $puid,
                        'password' => md5('888888'),
                        'phone' => $share_strlog['phone'],
                        'bili' => $agent_adrate['one_rate'],
                        'createtime' => time(),
                        'package_id' => $share_strlog['package_id'],
                        'channel' => $share_strlog['channel'],
                        'packname' => $share_strlog['appname'],
                        'nickname' => $nickname,
                        'avatar' => $share_strlog['avatar'],
                        'type' => $type,
                        'level' => $level_z,
                    ]);
                    $is_agent = 1;
                }

            }else{
                return $is_agent;
            }
        }

        //防止层级过多出问题，这里最多设置10级
        if($level >= 7)return $is_agent;

        //获取代理的上级用户,如果不存在直接返回
        $user_agent = Db::table('agent')->select('puid','bili')->where('uid',$puid)->first();
        if(!$user_agent)return $is_agent;
        self::installTeamLevel($uid,$puid,$user_agent['bili'],$level + 1);
        //如果推荐代理有上级用户，同时上级用户不是代理自己
        if($user_agent['puid'] > 0 && $user_agent['puid'] != $puid) self::setTeamLevel($uid,$user_agent['puid'],$type,$level + 1, $share_strlog, $level_z);
        return $is_agent;
    }

    /**
     * 储存用户团队层级数据
     * @return void 存储
     */
    public static function installTeamLevel($uid,$puid,$bili,$level = 0){
        Db::table('agent_teamlevel')->insert([
            'uid' => $uid,
            'puid' => $puid,
            'level' => $level,
            'bili' => $bili,
            'createtime' => time(),
        ]);
    }

    /**
     * 获取代理结算的开始结束时间戳
     * @param $time
     * @return array
     */
    public static function getStartEnd($time){
        $agent_time = self::getConfigValue('agent_time');
        $agent_time_arr = explode(',',$agent_time);
        $today = date('j');
//        $today = 25;

        foreach ($agent_time_arr as $key => $value) {
            if ($today >= $value) {
                $start_time = strtotime(date('Y-m-'.$value));
                if (isset($agent_time_arr[$key+1]) && $today < $agent_time_arr[$key+1]) {
                    $end_time = strtotime(date('Y-m-'.($agent_time_arr[$key+1]-1).' 23:59:59'));
                }else{
                    $end_time = strtotime(date('Y-m-t 23:59:59')); // 本月最后一天的结束时间
                }
            }
        }

        return [$start_time,$end_time];
    }

    /**
     * 判断用户是否有签到功能
     * @param $uid
     * @return bool
     */
    public static function isUserSign($uid){
        $is_open = self::getConfigValue('is_open_sign');
        if (!$is_open) return false;

        $userinfo = Db::table('userinfo as a')
            ->join('share_strlog as c', 'a.uid', '=', 'c.uid')
            ->where('a.uid', $uid)
            ->selectRaw('br_a.uid,br_a.total_pay_score,br_c.last_pay_time')
            ->first();
        if ($userinfo['last_pay_time'] > 0) { //连续5天不充值
            $last_time = (time() - $userinfo['last_pay_time']) / 86400;
        } else {
            $last_time = 6;
        }

        if (empty($userinfo) || $userinfo['total_pay_score'] == 0 || $last_time > 5) return false;

        $user_sign = Db::table('sign_in')->where('uid', $userinfo['uid'])->first();
        if ($user_sign) {
            $sign_date = date('Y-m-d', strtotime($user_sign['signTime']));
            //连续7天后，关闭签到功能
            if ($user_sign['num'] >= 7 && date('Y-m-d') > $sign_date) {
                return false;
            }
        }

        return true;
    }




    /**
     * 静态方法存储日志
     *
     * @param string $name
     * @param int|string $info
     */
    public static function selfLogError(string $name,int|string $info){
        self::$logger->error($name.':'.$info);
    }


    /**
     * 查看用户是否在自研游戏中
     * @param $uid 用户UID
     * @return int
     */
    public static function getZyOnlineUserStatus($uid){
        $Redis = self::Redis('Redis5511');
        $user_status =  $Redis->sIsMember('online_user',(string)$uid);
        return $user_status ? 1 : 0;
    }


    /**
     * 自研游戏必须进入60秒以后才能提现
     * @param $uid
     * @return int
     */
    public static function setZyWithdrawStatus($uid){
        $Redis = self::Redis('Redis5502');
        $time =  $Redis->hGet('user_'.$uid,'time');
        if(!$time)return 1;
        if(($time + 300 ) > time())return 0;
        return 1;
    }

    /**
     * 中间字符隐藏***处理
     * @param $string
     * @param $mask
     * @return string
     */
    public static function maskString($string, $mask = '***') {
        if (strlen($string) <= strlen($mask) + 2) {
            return $string; // 长度不足，直接返回
        }
        $prefix = substr($string, 0, 2);
        $suffix = substr($string, -3);
        return $prefix . $mask . $suffix;
    }

    /**
     * 设置免费次数
     * @param $uid
     * @param $game_id
     * @param $num
     * @return int
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \RedisException
     */
    public static function setFreeNum($uid, $game_id, $num)
    {
        $Redis5502 = Common::Redis('Redis5502');
        $old_num = $Redis5502->hGet('user_'.$uid, $game_id.'_free_num');
        $num = $old_num > 0 ? $old_num + $num : $num;
        $free_num_time = Common::getConfigValue('free_num_time');

        $data = [
            $game_id.'_free_num' => $num,
            $game_id.'_free_time' => time() + $free_num_time,//(86400*5),
        ];
        $Redis5502->hMSet('user_'.$uid, $data);
        $Redis5502->expire('user_'.$uid, 3600*24*5);

        return 1;
    }

    /**
     * 设置转盘
     * @param $uid
     * @return void
     */
    public static function setTurntable($order, $puid)
    {
        $user_team = Db::table('user_team')->where('uid', $order['uid'])->first();
        if (empty($user_team)) return 1;

        //如果是转盘邀请，增加上级转盘次数
        if ($user_team['turntable_id'] > 0){
            Db::table('turntable')->where('id',$user_team['turntable_id'])->increment('z_count', 1);
        }

        //首充金额百分比返利
        $share_rule = Db::table('share_rule')->first();
        $bill = bcdiv((string)$share_rule['first_deposit_rate'], '100',3);
        $pay_commission = bcmul((string)$order['price'], (string)$bill);
        Db::table('commission_log')->insert([
            'uid' => $order['uid'],
            'puid' => $puid,
            'bill' => $bill,
            'BetAmount' => $order['price'],
            'commission' => $pay_commission,
            'createtime' => time(),
            'dataType' => 2,
        ]);
        User::userEditCoin($puid, $pay_commission, 43, '分享首充奖励', 4);
        User::editUserTotalGiveScore($puid, $pay_commission);

        //判断每日发放奖励次数还有没有 发放奖励
        $today_s = strtotime('today');
        $today_e = strtotime('tomorrow') - 1; // 今日 23:59:59 的时间戳
        $share_config = Common::getMore('share_get_cash,share_get_cash_new,share_dayget_num');
        $today_team_num = Db::table('user_team')->where('puid',$puid)->where('pstatus',1)->whereBetween('createtime',[$today_s, $today_e])->count();
        if ($today_team_num < $share_config['share_dayget_num']){//今天邀请的有效 小于 每天固定数量
            //上级获得
            Db::table('commission_log')->insert([
                'uid' => $order['uid'],
                'puid' => $puid,
                'commission' => $share_config['share_get_cash'],
                'createtime' => time(),
                'dataType' => 3,
            ]);
            User::userEditCoin($puid, $share_config['share_get_cash'], 44, '有效邀请奖励', 4);
            User::editUserTotalGiveScore($puid, $share_config['share_get_cash']);

            //自己获得
            Db::table('commission_log')->insert([
                'uid' => $order['uid'],
                'puid' => $order['uid'],
                'commission' => $share_config['share_get_cash_new'],
                'createtime' => time(),
                'dataType' => 3,
            ]);
            User::userEditCoin($order['uid'], $share_config['share_get_cash_new'], 44, '有效邀请奖励', 4);
            User::editUserTotalGiveScore($order['uid'], $share_config['share_get_cash_new']);
        }

        //判断邀请任务
        $team_num = Db::table('user_team')->where('puid',$puid)->where('pstatus',1)->count();
        $team_num += 1;
        $share_task = Db::table('share_task')->where('number', $team_num)->first();
        if (!empty($share_task)){
            Db::table('commission_log')->insert([
                'uid' => $order['uid'],
                'puid' => $puid,
                'commission' => $share_task['cash'],
                'createtime' => time(),
                'dataType' => 4,
            ]);
            User::userEditCoin($puid, $share_task['cash'], 45, '邀请任务奖励', 4);
            User::editUserTotalGiveScore($puid, $share_task['cash']);
        }

        //标记有效用户
        Db::table('user_team')->where('uid', $order['uid'])->update(['pstatus'=>1]);

        return 1;
    }

    /**
     * 新手充值提现、rtp触发记录
     * @param $userinfo 用户数据
     * @param $type 数据类型 1-触发rtp 2-充值 3-提现
     * @param $val 充值提现时为金额   触发rtp时为rtp
     * @return int
     */
    public static function setNewUser($userinfo, $type, $val)
    {
        if (empty($userinfo)) return 1;
        /*$reg = Carbon::createFromTimestamp($userinfo['regist_time']);
        $days = $reg->diffInDays(Carbon::now()) + 1;*/
        $days = Common::getTimeDays($userinfo['regist_time']);
        if ($days > 4) return 1;//注册超过4天不管
        $user_type = \App\Controller\user\UserController::getUserType($userinfo['uid']);
        $gl_uid = My::activeBeforeGlUidStatus($userinfo['uid']);
        if ($user_type!=1 && $gl_uid==0) return 1;

        $rtp_type = 99;
        $money = 0;
        switch ($type){
            case 1:
                $rtp_type = $val;
                break;

            default:
                $money = $val;
                break;
        }
        $data = [
            'uid' => $userinfo['uid'],
            'type' => $type,
            'rtp_type' => $rtp_type,
            'coin' => $userinfo['coin'],
            'bonus' => $userinfo['bonus'],
            'water_multiple' => $userinfo['total_pay_score'] > 0 ? bcdiv((string)$userinfo['total_cash_water_score'], (string)$userinfo['total_pay_score'], 2) : 0,
            'loss' => $userinfo['total_pay_score'] - $userinfo['total_exchange'] - $userinfo['coin'],
            'total_pay_score' => $userinfo['total_pay_score'],
            'total_exchange' => $userinfo['total_exchange'],
            'total_cash_water_score' => $userinfo['total_cash_water_score'],
            'money' => $money,
            'createtime' => time(),
        ];

        $res = Db::table('user_rtpev')->insert($data);
        return 1;
    }

    public static function getTimeDays($registerTimestamp)
    {
        $reg = Carbon::createFromTimestamp($registerTimestamp);
        $days = $reg->diffInDays(Carbon::now()) + 1;
        return $days;
    }

    /**
     * 生成唯一ID字符串
     * @param int $suffixLen 随机后缀长度
     * @param string $prefix 前缀
     * @param int $upperOrLower 1大写、2小写
     * @return string
     */
    public static function createIdSn(int $suffixLen = 5, string $prefix = '', int $upperOrLower = 1, array $existIds = []): string
    {
        $str = 'abcdefghijklmnopqrstuvwxyz0123456789';
        $code = date('ymdHis') . substr(microtime(), 2, 3);
        for ($i = 1; $i <= $suffixLen; $i++) {
            $code .= $str[mt_rand(0, strlen($str)-1)];
        }

        // 防止生成重复ID
        if ($existIds && in_array($code, $existIds)) {
            return self::createIdSn($suffixLen, $prefix, $upperOrLower, $existIds);
        }
        unset($existIds);

        return $upperOrLower === 1 ? strtoupper($prefix . $code) : strtolower($prefix . $code);
    }



    /**
     * 获取包的ID与同一平台的所有ID
     * @param $packname 包名
     * @return array
     */
    public static function getPackageIdAndGlPackageId($packname)
    {
        $apppackage = Db::table('apppackage')->selectRaw('pro_name,id')->where('appname',$packname)->first();
        if(!$apppackage){
            return [0,[]];
        }
        $package_id = $apppackage['id'];
        $gl_package_id_array = Db::table('apppackage')->selectRaw('pro_name,id')->where('pro_name',$apppackage['pro_name'])->pluck('id')->toArray();
        return [$package_id,$gl_package_id_array];
    }


}